package com.leenmvc.core.dao.mysql;

import com.leenmvc.core.annotation.PrimaryKey;
import com.leenmvc.core.annotation.TableField;
import com.leenmvc.core.annotation.validator.groups.AddGroup;
import com.leenmvc.core.annotation.validator.groups.DeleteGroup;
import com.leenmvc.core.annotation.validator.groups.UpdateGroup;
import com.leenmvc.core.base.BaseEntity;
import com.leenmvc.core.dao.communal.BaseDao;
import com.leenmvc.core.dao.mysql.wrapper.UpdateWrapper;
import com.leenmvc.core.dao.communal.Wrapper;
import com.leenmvc.core.exception.BusinessException;
import com.leenmvc.core.exception.DaoException;
import com.leenmvc.core.exception.ValidatorException;
import com.leenmvc.core.security.User;
import com.leenmvc.core.utils.ClassUtil;
import com.leenmvc.core.utils.ConvertUtils;
import com.leenmvc.core.utils.TipUtil;
import com.leenmvc.core.utils.collection.MapUtils;
import com.leenmvc.core.utils.unique.SnowFlake;
import com.leenmvc.core.utils.validator.ValidatorUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 在执行持久层方法之前对传入的数据进行校验的类
 * 保证传入持久层的参数都是可以被接收的。
 */
public class MysqlBeforeExcute implements BaseDao {

    private BaseDao baseDao = MysqlBaseDaoImpl.getInstance();

    @Override
    public <T> List<T> getPageList(Wrapper wrapper) {

        return null;
    }

    @Override
    public boolean deleteById(Class<?> cls, Object id) {
        Object o = baseDao.get(cls, id);
        if (o == null)
            return false;
        ValidatorUtils.validateEntity(o, DeleteGroup.class);
        return false;
    }

    @Override
    public int delete(Class<?> cls, Object... kvs) {
        List<Object> list = baseDao.getList(cls, kvs);
        for (Object o : list) {
            ValidatorUtils.validateEntity(o, DeleteGroup.class);
        }
        return 0;
    }

    @Override
    public int deleteByIds(Class<?> cls, Object[] ids) {
        for (Object id : ids) {
            Object o = baseDao.get(cls, id);
            ValidatorUtils.validateEntity(o, DeleteGroup.class);
        }
        return ids.length;
    }

    @Override
    public <T> boolean remove(T t) {
        ValidatorUtils.validateEntity(t, DeleteGroup.class);
        return false;
    }

    @Override
    public <T> int batchRemove(List<T> list) {
        for (T t : list) {
            ValidatorUtils.validateEntity(t, DeleteGroup.class);
        }
        return 0;
    }

    @Override
    public <T> T get(Class<?> cls, Object id) {
        Class<?> type = ClassUtil.getPrimaryField(cls).getType();
        ValidatorUtils.notNull(TipUtil.getTip("NOTNULL_FAIL"), id);
        ValidatorUtils.convertToTargetType("参数类型错误，请检查", id, type);
        return null;
    }

    @Override
    public <T> T get(Class<?> cls, Object... kvs) {
        checkKvs(cls, null, kvs);
        return null;
    }

    @Override
    public <T> List<T> getList(Class<?> cls, Object... kvs) {
        checkKvs(cls, null, kvs);
        return null;
    }

    @Override
    public <T> List<T> getList(Class<?> cls, Map<String, Object> params) {
//        checkHashMap(cls, null, params);
        return null;
    }

    @Override
    public <T> T get(Class<?> cls, Map<String, Object> params) {
        if (params.size() == 0) {
            throw new ValidatorException("通过hashmap获取一个对象时必须给hashmap传入至少一个值", false);
        }
        checkHashMap(cls, null, params);
        return null;
    }

    @Override
    public <T> List<T> getPageList(Class<?> cls, Integer... pageInfo) {
        return null;
    }

    @Override
    public <T> List<T> getPageList(Class<?> cls, Map<String, Object> conditions, Integer... pageInfo) {
        checkHashMap(cls, null, conditions);
        return null;
    }

    @Override
    public <T> List<T> getAll(Class<?> cls) {
        return null;
    }

    @Override
    public <T> List<T> query(Class<?> cls, String sql, Object... objects) {
        return null;
    }

    @Override
    public Map<String, Object> getMap(Class<?> cls, String sql, Object... objects) {
        return null;
    }

    @Override
    public List<Map<String, Object>> getListMap(Class<?> cls, String sql, Object... objects) {
        return null;
    }

    /**
     * 校验通过实体插入数据
     *
     * @param t
     * @param <T>
     * @return
     */
    @Override
    public <T> boolean insert(T t) {
        Object primaryFieldValue = ClassUtil.getPrimaryFieldValue(t);
        if (primaryFieldValue == null) {
            if (!insertPrimaryKey(t)) {
                throw new DaoException("设置主键到" + t.getClass().getSimpleName() + "失败");
            }
            ;
        } else if (primaryFieldValue != null) {
            int id = baseDao.count(t.getClass(), "id", primaryFieldValue);
            if (id > 0) {
                throw new BusinessException(TipUtil.getTip("PRIMARY_CONFLICT", primaryFieldValue));
            }
        }
        insertCommon(t);
        ValidatorUtils.validateEntity(t, AddGroup.class);
        return true;
    }

    @Override
    public <T> boolean insert(Class<?> cls, Map<String, Object> entityMap) {

        // 设置主键
        String primaryFieldName = BaseEntity.getPrimaryFieldName(cls);
        Object primaryFieldValud = entityMap.get(primaryFieldName);
        if (primaryFieldValud == null) {
            if (insertPrimaryKey(cls, entityMap)) {
                throw new DaoException("设置主键到" + cls.getSimpleName() + "失败");
            }
        } else if (primaryFieldValud != null) {
            int id = baseDao.count(cls, "id", primaryFieldValud);
            if (id > 0) {
                throw new DaoException(TipUtil.getTip("PRIMARY_CONFLICT", primaryFieldValud));
            }
        }

        // 设置创建时间
        if (insertCommon(cls, entityMap)) {

        }

        Object o = ClassUtil.mapToEntity(cls, entityMap);
        ValidatorUtils.validateEntity(o, AddGroup.class);
        return true;
    }

    @Override
    public <T> boolean batchInsert(List<T> ts) {
        for (T t : ts) {
            Object primaryFieldValue = ClassUtil.getPrimaryFieldValue(t);
            if (primaryFieldValue == null) {
                if (insertPrimaryKey(t)) {
                    throw new DaoException("设置主键到" + t.getClass().getSimpleName() + "失败");
                }
            }

            insertCommon(t.getClass());

            ValidatorUtils.validateEntity(t, AddGroup.class);
        }
        return true;
    }

    @Override
    public <T> boolean saveOrUpdate(T t) {
        Object primaryFieldValue = ClassUtil.getPrimaryFieldValue(t);
        if (primaryFieldValue == null) {
            if (!insertPrimaryKey(t)) {
                throw new DaoException("设置主键到" + t.getClass().getSimpleName() + "失败");
            }
            ;
            ValidatorUtils.validateEntity(t, AddGroup.class);
        } else {
            ValidatorUtils.validateEntity(t, UpdateGroup.class);
        }
        return false;
    }

    @Override
    public <T> boolean batchInsert(T[] ts) {
        for (T t : ts) {
            Object primaryFieldValue = ClassUtil.getPrimaryFieldValue(t);
            if (primaryFieldValue == null) {
                if (insertPrimaryKey(t)) {
                    throw new DaoException("设置主键到" + t.getClass().getSimpleName() + "失败");
                }
                ;
            }
            insertCommon(t);
            ValidatorUtils.validateEntity(t, AddGroup.class);
        }
        return false;
    }

    @Override
    public <T> boolean update(T t) {
        updateCommon(t);
        ValidatorUtils.validateEntity(t, UpdateGroup.class);
        return false;
    }

    @Override
    public <T> boolean update(Class<?> cls, Object id, Object... kv) {
        ValidatorUtils.notNull("更新" + cls.getSimpleName() + "时id为空！", id);
        if (kv.length % 2 != 0) return false;
        Object o = baseDao.get(cls, id);
        ValidatorUtils.notNull("更新失败，给定的ID无效", o);
        for (int x = 0; x < kv.length; x += 2) {
            try {
                Method setMethod = ClassUtil.getSetMethod(cls, kv[x].toString());
                setMethod.invoke(o, kv[x + 1]);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        updateCommon(o);

        ValidatorUtils.validateEntity(o, UpdateGroup.class);
        return false;
    }

    @Override
    public <T> int update(Class<?> cls, Map<String, Object> params) {
        for (String key : params.keySet()) {
            if (key.equals(BaseEntity.getPrimaryFieldName(cls))) continue;
            ValidatorUtils.validateSingleField(ClassUtil.getField(cls, key), params, UpdateGroup.class);
        }
        updateCommon(cls, params);
        return 0;
    }

    @Override
    public int count(Class<?> cls, Map<String, Object> params) {
        checkHashMap(cls, null, params);
        return 0;
    }

    @Override
    public int count(Class<?> cls, Object... kvs) {
        checkKvs(cls, null, kvs);
        return 0;
    }

    @Override
    public int count(Class<?> cls) {
        return 0;
    }

    @Override
    public int count(Wrapper wrapper) {
        return 0;
    }

    @Override
    public <T> List<T> query(Wrapper wrapper) {
        return null;
    }

    @Override
    public <T> T get(Wrapper wrapper) {
        return null;
    }

    @Override
    public <T> T getOneField(Wrapper queryWrapper) {
        return null;
    }

    @Override
    public <T> List<T> oneFieldList(Wrapper queryWrapper) {
        return null;
    }

    @Override
    public Map<String, Object> getMap(Wrapper wrapper) {
        return null;
    }

    @Override
    public List<Map<String, Object>> getListMap(Wrapper wrapper) {
        return null;
    }

    @Override
    public int delByWrapper(Wrapper wrapper) {
        return 0;
    }

    @Override
    public int updateByWrapper(Wrapper wrapper) {
        UpdateWrapper w = (UpdateWrapper) wrapper;
        if (ClassUtil.isExistField(wrapper.getCls(), "updater")) {
            w.addSet("updater", User.getId());
        }

        if (ClassUtil.isExistField(wrapper.getCls(), "updateDate")) {
            w.addSet("updateDate", new Date());
        }

        return 0;
    }

    @Override
    public <T> List<T> listBySqlKey(Class<?> cls, String batisKey, Map<String, Object> params) {
        return null;
    }

    @Override
    public <T> T getBySqlKey(Class<?> cls, String id, Map<String, Object> params) {
        return null;
    }

    @Override
    public <T> List<T> listBySqlKey(Class<?> cls, String batisKey, Object params) {
        return null;
    }

    @Override
    public <T> T getBySqlKey(Class<?> cls, String id, Object params) {
        return null;
    }

    @Override
    public <T> List<T> listBySqlKey(Class<?> cls, String batisKey, Object... KVs) {
        return null;
    }

    @Override
    public <T> T getBySqlKey(Class<?> cls, String id, Object... KVs) {
        return null;
    }

    /**
     * 插入主键
     *
     * @param arg
     * @return boolean 是否新增了主键
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private synchronized boolean insertPrimaryKey(Object arg) {
        Class<?> aClass = arg.getClass();
        Method primarySetMethod = ClassUtil.getPrimarySetMethod(aClass);
        try {
            Field primaryField = ClassUtil.getPrimaryField(aClass);
            PrimaryKey primaryKey = primaryField.getAnnotation(PrimaryKey.class);
            if (primaryKey != null) {
                switch (primaryKey.uniqueType()) {
                    case SNOW:// 雪花算法
                        Long snowId = SnowFlake.generateId();
                        primarySetMethod.invoke(arg, ConvertUtils.convertType(snowId, primaryField.getType()));
                        break;
                    case UUID: // UUID
                        primarySetMethod.invoke(arg, UUID.randomUUID());
                        break;
                    case INCREMENT: // 自增
                        break;
                }
                return true;
            }
        } catch (IllegalAccessException e) {
            throw new DaoException(e);
        } catch (InvocationTargetException e) {
            throw new DaoException(e);
        }
        return false;
    }

    /**
     * 插入操作设置主键 hashmap作为参数
     *
     * @param aClass
     * @param entityMap
     * @return
     */
    private synchronized boolean insertPrimaryKey(Class<?> aClass, Map<String, Object> entityMap) {
        String primaryFieldName = BaseEntity.getPrimaryFieldName(aClass);
        Field primaryField = ClassUtil.getPrimaryField(aClass);
        PrimaryKey primaryKey = primaryField.getAnnotation(PrimaryKey.class);
        if (primaryKey != null) {
            switch (primaryKey.uniqueType()) {
                case SNOW:// 雪花算法
                    entityMap.put(primaryFieldName, SnowFlake.generateId());
                    break;
                case UUID: // UUID
                    entityMap.put(primaryFieldName, UUID.randomUUID());
                    break;
                case INCREMENT: // 自增
                    break;
            }
        }
        return true;
    }

    private boolean insertCommon(Object obj) {
        Class<?> cls = obj.getClass();
        try {
            if (ClassUtil.isExistField(cls, "createDate")) {
                Method createMethod = ClassUtil.getSetMethod(cls, "createDate");
                createMethod.invoke(obj, new Date());
            }

            if (ClassUtil.isExistField(cls, "updateDate")) {
                Method updateMethod = ClassUtil.getSetMethod(cls, "updateDate");
                updateMethod.invoke(obj, new Date());
            }
            if (ClassUtil.isExistField(cls, "creator")) {
                Method updateMethod = ClassUtil.getSetMethod(cls, "creator");
                updateMethod.invoke(obj, User.getId());
            }

            if (ClassUtil.isExistField(cls, "updater")) {
                Method updateMethod = ClassUtil.getSetMethod(cls, "updater");
                updateMethod.invoke(obj, User.getId());
            }

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 更新操作的时候使用本方法将实体内公共字段设值
     *
     * @param cls
     * @param entityMap
     * @return
     */
    private boolean updateCommon(Class<?> cls, Map<String, Object> entityMap) {

        if (ClassUtil.isExistField(cls, "updateDate")) {
            entityMap.put("updateDate", new Date());
        }

        if (ClassUtil.isExistField(cls, "updater")) {
            entityMap.put("updater", User.getId());
        }
        return true;
    }

    /**
     * 更新操作的时候使用本方法将实体内公共字段设值
     *
     * @param obj
     * @return
     */
    private boolean updateCommon(Object obj) {
        Class<?> cls = obj.getClass();
        try {
            if (ClassUtil.isExistField(cls, "updateDate")) {
                Method updateMethod = ClassUtil.getSetMethod(cls, "updateDate");
                updateMethod.invoke(obj, new Date());
            }

            if (ClassUtil.isExistField(cls, "updater")) {
                Method updateMethod = ClassUtil.getSetMethod(cls, "updater");
                updateMethod.invoke(obj, User.getId());
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return true;
    }


    private boolean insertCommon(Class<?> cls, Map<String, Object> entityMap) {
        if (ClassUtil.isExistField(cls, "createDate")) {
            entityMap.put("createDate", new Date());
        }

        if (ClassUtil.isExistField(cls, "updateDate")) {
            entityMap.put("updateDate", new Date());
        }

        if (ClassUtil.isExistField(cls, "creator")) {
            entityMap.put("creator", User.getId());
        }

        if (ClassUtil.isExistField(cls, "updater")) {
            entityMap.put("updater", User.getId());
        }
        return true;
    }

    /**
     * 检查kvs中的字段在数据库中是否存在
     *
     * @param cls
     * @param kvs
     */
    private void checkKvs(Class<?> cls, Class<?> group, Object... kvs) {
        checkHashMap(cls, group, MapUtils.arrayToMap(kvs));
    }


    /**
     * 检查Map中的键值对在数据库中是否存在
     *
     * @param cls
     * @param params
     */
    private void checkHashMap(Class<?> cls, Class<?> group, Map<String, Object> params) {
        for (String key : params.keySet()) {
            try {
                Field targetField = cls.getDeclaredField(key);
                ValidatorUtils.validateSingleField(targetField, params.get(key), group);
                TableField annotation = targetField.getAnnotation(TableField.class);
                if (annotation == null) continue;
                if (annotation.value()) continue;
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
            throw new ValidatorException("参数错误，请检查！");
        }
    }
}
